package zero.semantic;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map.Entry;

import zero.entity.Symbol;
import zero.lex.Token;

/**
 * 语义分析
 * 
 * @author Zero
 *
 */
public class Semantic {
    private List<Quaternion>          quaternionList      = new ArrayList<>();
    private List<Token>               tokenList;
    private int                       nextQuaternionIndex = 1;
    private int                       index               = 0;                // 从列表中获取单词的下标
    private Token                     token;                                  // 当前扫描到的词
    private int                       tempVariousNum      = 1;                // 临时变量标号
    private int                       trueCondition       = 1;
    private int                       falseCondition      = 1;
    private Hashtable<String, Symbol> symbolTable         = new Hashtable<>();
    private int                       offset              = 0;

    public Semantic(List<Token> tokenList) {
        this.tokenList = tokenList;
        symbolTable.put("int", new Symbol("int", 4, null));
    }

    public void printQuaternions() {
        int i = 0;

        for (Quaternion q : quaternionList) {
            System.out.println(++i + " " + q.toString());
        }
    }

    public void createQuaternion(String op, String arg1, String arg2, String result) {
        quaternionList.add(new Quaternion(op, arg1, arg2, result));
        ++nextQuaternionIndex;
    }

    /**
     * 合并q1和q2，q1,q2是两个四元式在四元式表中的下标
     * 
     * @param q1
     * @param q2
     * @return
     */
    public int merge(int q1, int q2) {
        int result;
        if (q2 == 0) {
            result = q1;
        } else {
            result = q2;
            backFill(q2, q1);
        }
        return result;
    }

    /**
     * 将result回填到四元式表中下标为qIndex的四元式中的第四个元素中
     * 
     * @param qIndex
     * @param result
     */
    public void backFill(int qIndex, int result) {
        if (qIndex > 0) {
            quaternionList.get(qIndex).setResult(result + "");
        }
    }

    /**
     * 分析表达式
     */
    public String expression() {
        String op, arg1, arg2, result;
        arg1   = term();
        result = arg1;

        while (token.getContent().equals("+") || token.getContent().equals("-")) { // 加法 减法
            op     = token.getContent();
            token  = getNext();
            arg2   = term();
            result = newTemp();

            // result本应由arg1和arg2的继承属性来决定
            genSymbol(result, "int", 4);
            createQuaternion(op, arg1, arg2, result);
            arg1 = result;
        }
        return result;
    }

    public String term() {
        String op, arg1, arg2, result;

        result = arg1 = factor();
        while (token.getContent().equals("*") || token.getContent().equals("/")) {
            op     = token.getContent();
            token  = getNext();
            arg2   = factor();
            result = newTemp();
            genSymbol(result, "int", 4);
            createQuaternion(op, arg1, arg2, result);
            arg1 = result;
        }
        return result;
    }

    public boolean match(String content) {
        if (token.getContent().equals(content)) {
            token = getNext();
            return true;
        } else {
            System.out.printf("[Line %d, Column %d]: ", token.getLineNo(), token.getColumnNo());
            System.out.println("Expect: '" + content + "'; but get '" + token.getContent() + "'");
            return false;
        }
    }

    public String factor() {
        String result;
        if (token.getId() == 1 || token.getId() == 2) {
            result = token.getContent();
            token  = getNext();
        } else {
            if (!match("(")) {
                token = getNext();
            }
            result = expression();
            match(")");
        }
        return result;
    }

    public void condition() {
        String op, arg1, arg2;
        arg1 = expression();

        String word = token.getContent();

        if (word.equals(">") || word.equals("<") || word.equals(">=") || word.equals("<=") || word.equals("==")
                || word.equals("!=")) { // 关系运算符
            op             = word;
            token          = getNext();
            arg2           = expression();
            trueCondition  = nextQuaternionIndex;
            falseCondition = nextQuaternionIndex + 1;
            op             = "goto " + op;
            createQuaternion(op, arg1, arg2, "0");
            createQuaternion("goto", "", "", "0");
        } else {
            error(token, "关系运算符错误");
        }
    }

    public void define() {
        int    length = symbolTable.get("int").getLength();
        int    addr   = offset;
        String word   = token.getContent();

        if (symbolTable.containsKey(word)) {
            error(token, "变量重复定义");
        } else if (token.getId() != 1) {
            error(token, "类型后只能跟标识符");
        } else {
            Symbol symbol = new Symbol("int", length, addr);
            symbolTable.put(word, symbol);
            offset += length;
        }

        token = getNext();
    }

    public int statement(int chain) {
        String result, arg1;
        int    chainTemp = 0;
        int    tempFalse = 0;

        switch (token.getContent()) {
        case "if":
            match("if");
            match("(");

            condition();
            backFill(trueCondition, nextQuaternionIndex);

            match(")");

            tempFalse = falseCondition;
            chainTemp = statement_Block(chainTemp);
            chain = merge(chainTemp, tempFalse);
            break;
        case "while":
            match("while");
            int tempIndex = nextQuaternionIndex;

            match("(");

            condition();
            backFill(trueCondition, nextQuaternionIndex);

            match(")");

            tempFalse = falseCondition;

            chainTemp = statement_Block(chainTemp);
            backFill(chainTemp, tempIndex);
            result = "" + tempIndex;
            createQuaternion("goto", "", "", result);
            chain = tempFalse;
            break;
        case "int":
            match("int");
            define();
            match(";");
            chain = 0;
            break;
        default:
            if (token.getId() == 1) {
                result = token.getContent();
                if (!symbolTable.containsKey(result)) {
                    error(token, "未定义的标识符");
                }

                token = getNext();

                match("=");

                if (token.getContent().equals(";")) {
                    error(token, "'='右部不能为空");
                    jumpError();
                } else {
                    arg1 = expression();
                    match(";");
                    createQuaternion("=", arg1, "", result);
                }

                chain = 0;
            } else if (token.getId() == 2) {
                error(token, "常数不能作为赋值表达式的左部");
            } else {
                error(token, "语句有误");
                jumpError();
            }
            break;
        }
        return chain;
    }

    public int statement_Sequence(int chain) {
        chain = statement(chain);

        while (token.getId() == 1 || token.getContent().equals("if") || token.getContent().equals("while")
                || token.getContent().equals("int")) {
            backFill(chain, nextQuaternionIndex);
            chain = statement(chain);
        }

        backFill(chain, nextQuaternionIndex);

        return chain;
    }

    public int statement_Block(int chain) {
        match("{");
        chain = statement_Sequence(chain);
        match("}");

        return chain;
    }

    /**
     * 取下一个词，如果是读取到换行，就增加行数，
     * 
     * @param list
     * @return
     */
    public Token getNext() {
        if (index < tokenList.size()) {
            Token currentToken = tokenList.get(index++);
            return currentToken;
        }

        return null;
    }

    public void analyze() {
        int chain = 0;

        token = getNext();
        match("void");
        match("main");
        match("(");
        match(")");

        symbolTable.put("main", new Symbol("function", 4, offset));
        offset += 4;

        statement_Block(chain);

        if (index != tokenList.size()) {
            System.out.println("程序非正常结束！");
        }

        System.out.println("\n四元式序列:");
        printQuaternions();

        System.out.println("\n中间代码:");
        printCode();

        outputSymbol();
    }

    public void jumpError() {
        while (!token.getContent().equals(";") && index < tokenList.size()) {
            token = getNext();
        }
        if (index < tokenList.size()) {
            token = getNext();
        }
    }

    public void printCode() {
        for (int i = 1; i < quaternionList.size(); i++) {
            Quaternion quaternion = quaternionList.get(i);
            System.out.printf("%-3d ", i);

            if (quaternion.getOp().length() == 1) {
                if (quaternion.getArg2().trim().length() > 0) {
                    System.out.println(quaternion.getResult() + " = " + quaternion.getArg1() + quaternion.getOp()
                            + quaternion.getArg2());
                } else {
                    System.out.println(quaternion.getResult() + " " + quaternion.getOp() + " " + quaternion.getArg1()
                            + quaternion.getArg2());
                }
            } else if (quaternion.getOp().length() == 4) {
                System.out.println(quaternion.getOp() + " " + quaternion.getResult());
            } else {
                String[] token = quaternion.getOp().split(" ");
                System.out.println("if (" + quaternion.getArg1() + " " + token[1] + " " + quaternion.getArg2() + ") "
                        + token[0] + " " + quaternion.getResult());
            }
        }
    }

    public String newTemp() {
        return "t" + (tempVariousNum++);
    }

    public void error(Token token, String msg) {
        System.err.printf("[Line %d Column %d] '%s': %s\n", token.getLineNo(), token.getColumnNo(), token.getContent(),
                msg);
    }

    public void outputSymbol() {
        System.out.println("\n符号表:");
        for (Entry<String, Symbol> entry : symbolTable.entrySet()) {
            System.out.println(entry.getKey() + " " + entry.getValue().toString());
        }
    }

    public int genSymbol(String variable, String type, int length) {
        int addr = offset;

        symbolTable.put(variable, new Symbol(type, length, addr));
        offset += length;

        return addr;
    }
}
